package com.zmn.oms.zmn.business.impl.xno;

import com.alibaba.fastjson.JSON;
import com.zmn.base.common.data.common.dro.area.AreaDRO;
import com.zmn.base.common.data.dubbo.interfaces.area.AreaListRemoteService;
import com.zmn.base.plat.engine.common.constant.DubboConsts;
import com.zmn.common.dto2.ResponseDTO;
import com.zmn.common.utils.collection.CollectionUtil;
import com.zmn.common.utils.date.DateUtil;
import com.zmn.common.utils.number.NumberUtil;
import com.zmn.common.utils.string.StringUtil;
import com.zmn.consts.GlobalConsts;
import com.zmn.oms.common.annotation.ZsNormalOrderWorkLog;
import com.zmn.oms.common.constant.OrderConsts;
import com.zmn.oms.common.constant.OrderLogConsts;
import com.zmn.oms.common.constant.RedisKeyConsts;
import com.zmn.oms.common.utils.MobileUtil;
import com.zmn.oms.model.dto.work.xno.XnoBindDTO;
import com.zmn.oms.model.dto.work.xno.XnoUnBindDTO;
import com.zmn.oms.model.entity.detail.OrderDetail;
import com.zmn.oms.model.entity.work.OrderWork;
import com.zmn.oms.services.interfaces.detail.OrderDetailService;
import com.zmn.oms.services.interfaces.work.OrderWorkService;
import com.zmn.oms.zmn.business.interfaces.xno.XnoBindBService;
import com.zmn.xno.dubbo.interfaces.bind.AxbBindRemoteService;
import com.zmn.xno.dubbo.model.bind.AxbBindDIO;
import com.zmn.xno.dubbo.model.bind.AxbBindDRO;
import com.zmn.xno.dubbo.model.bind.AxbUnbindDIO;
import com.zmn.xno.dubbo.model.bind.AxbUnbindDRO;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.*;

/**
 * @title: 小号绑定实现类
 * @author:Rananquan
 * @date:2022/7/19 11:16
 */
@Slf4j
@Service
public class XnoBindBServiceImpl implements XnoBindBService {
    private static final String TAG = "小号";

    private static final String APP_KEY = "oms";

    @Resource
    private OrderDetailService orderDetailService;

    @Resource
    private OrderWorkService orderWorkService;

    @DubboReference(version = com.zmn.base.plat.engine.common.constant.DubboConsts.INTERFACE_VERSION , check = false)
    AreaListRemoteService areaListRemoteService;

    @DubboReference(version = DubboConsts.INTERFACE_VERSION, check = false)
    AxbBindRemoteService axbBindRemoteService;

    @Resource
    private RedisTemplate<String, OrderDetail> orderDetailRedisTemplate;

    @Resource
    private RedisTemplate<String, OrderWork> orderWorkRedisTemplate;

    @Resource
    private RedisTemplate<String,String> redisTemplate;

    @Resource
    private RedisTemplate<String,Map<String,String>> mapRedisTemplate;

    ThreadPoolExecutor executor = new ThreadPoolExecutor(5,6,5,
            TimeUnit.SECONDS,
            new LinkedBlockingDeque<>(6),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.DiscardOldestPolicy());

    /**
     * 绑定
     * @param xnoBindDTO
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @ZsNormalOrderWorkLog(type = OrderLogConsts.ORDER_LOG_TYPE_XNO_BIND, beforeProceed = false)
    @Override
    public Boolean bind(XnoBindDTO xnoBindDTO){
        // 获取工单详情
        OrderDetail orderDetail = this.getOrderDetail(xnoBindDTO.getOrderId());

        // 获取工单
        OrderWork orderWork = this.getOrderWork(xnoBindDTO.getOrderId(), xnoBindDTO.getWorkId());

        // 绑定
        CompletableFuture<Boolean> bindFuture = CompletableFuture.supplyAsync(() -> {
            Boolean bindResult = this.bind(xnoBindDTO.getMasterMobile(), orderWork, orderDetail);
            if(!bindResult){
                return Boolean.FALSE;
            }

           // 修改工单详情小号
            return updateOrderDetail(orderDetail);
        }, executor);

        try {
            CompletableFuture.anyOf(bindFuture).join();

            return bindFuture.get();
        } catch (InterruptedException | ExecutionException e) {
            log.error("[{}]，e：{}",TAG,e.getMessage(),e);

            return Boolean.FALSE;
        }
    }


    /**
     * 解绑
     * @param xnoBindDTO
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @ZsNormalOrderWorkLog(type = OrderLogConsts.ORDER_LOG_TYPE_XNO_UNBIND, beforeProceed = false)
    @Override
    public Boolean unBind(XnoUnBindDTO xnoBindDTO) {
        // 如果未绑定或者已解绑则不需要解绑
        if(Objects.equals(xnoBindDTO.getBindStatus(),GlobalConsts.NONE) || Objects.equals(xnoBindDTO.getBindStatus(),OrderConsts.BIND_STATUS_UNBIND)){
            return Boolean.TRUE;
        }

        // 获取工单
        OrderWork orderWork = this.getOrderWork(xnoBindDTO.getOrderId(), xnoBindDTO.getWorkId());

        // 解绑小号
        return this.unBind(xnoBindDTO.getMasterMobile(), xnoBindDTO, String.valueOf(orderWork.getOrderId()), orderWork.getPlatWork(), orderWork.getCityId());
    }


    private AxbBindDIO setAxbBindDIO(String masterMobile,OrderWork orderWork,String mobile){
        return  AxbBindDIO.builder()
                .ano(masterMobile)
                .appKey(APP_KEY)
                .atype(1)
                .bizId(orderWork.getOrderId().toString())
                .bno(mobile)
                .btype(2)
                .plat(orderWork.getPlatWork())
                .cityId(orderWork.getCityId())
                .level1Id(orderWork.getPlatWork())
                .level2Id(orderWork.getManageCompanyId())
                .level3Id(orderWork.getCityId())
                .engineerId(orderWork.getMasterId())
                .engineerName(orderWork.getMasterName())
                .build();
    }


    /**
     * 判断电话是否是座机
     * @param telephone
     * @param code
     * @return
     */
    private String isFixedPhone(String telephone,String code){
        if(MobileUtil.isFixedPhone(telephone)){
            return code + telephone;
        }

        return telephone;
    }


    private Boolean bind(String masterMobile,OrderWork orderWork,OrderDetail orderDetail){
        Map<String,String> telephoneMap = new HashMap<>(3);

        String code = this.getAreaNum(orderWork.getCityId());

        CompletableFuture<Void> bindMobileFuture = CompletableFuture.runAsync(() -> {
            // 小号电话1 绑定
            String bindMobile = axbBind(masterMobile, orderWork, orderDetail.getTelephone(), code);

            if (StringUtil.isNotBlank(bindMobile)) {
                telephoneMap.put("bindMobile", bindMobile);
            }
        },executor);

        CompletableFuture<Void> bindMobileFuture2 = CompletableFuture.runAsync(() -> {
            // 小号电话2 绑定
            String bindMobile = axbBind(masterMobile, orderWork, orderDetail.getTelephone2(), code);

            if (StringUtil.isNotBlank(bindMobile)) {
                telephoneMap.put("bindMobile2", bindMobile);
            }
        }, executor);

        CompletableFuture<Void> bindMobileFuture3 = CompletableFuture.runAsync(() -> {
            // 小号电话3 绑定
            String bindMobile = axbBind(masterMobile, orderWork, orderDetail.getTelephone3(), code);

            if (StringUtil.isNotBlank(bindMobile)) {
                telephoneMap.put("bindMobile3", bindMobile);
            }
        }, executor);

        CompletableFuture.allOf(bindMobileFuture,bindMobileFuture2,bindMobileFuture3).join();

        if(CollectionUtil.isNullOrEmpty(telephoneMap)){
            return Boolean.FALSE;
        }

        // 缓存绑定的小号
        mapRedisTemplate.opsForValue().set(String.format(RedisKeyConsts.XNO_BIND_ORDER_TELEPHONE,orderDetail.getDetailId()),telephoneMap,10,TimeUnit.MINUTES);

        return Boolean.TRUE;
    }

    /**
     * 调用绑定接口
     * @param masterMobile
     * @param orderWork
     * @param telephone
     * @return
     */
    private String axbBind(String masterMobile,OrderWork orderWork,String telephone,String code) {
        if(StringUtil.isBlank(telephone)){
            return GlobalConsts.SPLITER_EMPTY;
        }
        String fixedPhone = isFixedPhone(telephone, code);
        try {
            AxbBindDIO axbBindDIO = setAxbBindDIO(masterMobile, orderWork, fixedPhone);
            log.info("[{}]绑定入参：{}",TAG,JSON.toJSONString(axbBindDIO));

            ResponseDTO<AxbBindDRO> bindResponse = axbBindRemoteService.bindX(axbBindDIO);
            if (!bindResponse.isSuccess() || Objects.isNull(bindResponse.getData())) {
                log.error("[{}]绑定请求失败，失败原因：{}", TAG, bindResponse.getMessage());

                return GlobalConsts.SPLITER_EMPTY;
            }

            AxbBindDRO bindData = bindResponse.getData();
            log.info("[{}]绑定返回response：{}", TAG,JSON.toJSONString(bindData));

            return bindData.getXno();
        }catch (Exception e){
            return GlobalConsts.SPLITER_EMPTY;
        }
    }


    /**
     * 查询绑定关系
     * @param masterMobile
     * @param telephone
     * @param code
     * @param orderId
     * @param platWork
     * @return
     */
    private Long queryXnoBind(String masterMobile,String telephone,String code,String orderId,Integer platWork){
        if(StringUtil.isBlank(telephone)){
            return null;
        }
        String fixedPhone = isFixedPhone(telephone, code);
        log.info("[{}]，查询绑定关系入参,fixedPhone：{}，masterMobile:{}",TAG,fixedPhone,masterMobile);
        ResponseDTO<Long> bindResponse = axbBindRemoteService.queryBindIdByXnoAndAno(fixedPhone, masterMobile);
        if(!bindResponse.isSuccess() || NumberUtil.isNullOrZero(bindResponse.getData())){
            log.error("[{}]，查询绑定关系请求失败，失败原因：{}",TAG,bindResponse.getMessage());

            return null;
        }

        Long bindId = bindResponse.getData();
        log.info("[{}]，查询绑定关系返回response：{}",TAG,bindId);

        return bindId;
    }


    /**
     * 调用解绑接口
     * @param masterMobile
     * @param xnoBindDTO
     * @param orderId
     * @param platWork
     * @param cityId
     * @return
     */
    private Boolean unBind(String masterMobile, XnoUnBindDTO xnoBindDTO, String orderId, Integer platWork, Integer cityId){
        // 获取区号
        String code = this.getAreaNum(cityId);

        List<AxbUnbindDIO> axbUnbindDIOList = new ArrayList<>(3);

        // 客户电话绑定ID1
        CompletableFuture<Void> queryXnoBindFuture = CompletableFuture.runAsync(() -> {
            Long xnoBindId = this.queryXnoBind(masterMobile, xnoBindDTO.getUnBindTelephone1(), code, orderId, platWork);

            if (NumberUtil.isNotNullOrZero(xnoBindId)) {
                AxbUnbindDIO axbUnbindDIO = AxbUnbindDIO.builder().bindId(xnoBindId).build();
                axbUnbindDIOList.add(axbUnbindDIO);
            }
        },executor);

        // 客户电话绑定ID2
        CompletableFuture<Void> queryXnoBindFuture2 = CompletableFuture.runAsync(() -> {
            Long xnoBindId2 = this.queryXnoBind(masterMobile, xnoBindDTO.getUnBindTelephone2(), code, orderId, platWork);

            if (NumberUtil.isNotNullOrZero(xnoBindId2)) {
                AxbUnbindDIO axbUnbindDIO = AxbUnbindDIO.builder().bindId(xnoBindId2).build();
                axbUnbindDIOList.add(axbUnbindDIO);
            }
        }, executor);

        // // 客户电话绑定ID3
        CompletableFuture<Void> queryXnoBindFuture3 = CompletableFuture.runAsync(() -> {
            Long xnoBindId3 = this.queryXnoBind(masterMobile, xnoBindDTO.getUnBindTelephone3(), code, orderId, platWork);

            if (NumberUtil.isNotNullOrZero(xnoBindId3)) {
                AxbUnbindDIO axbUnbindDIO = AxbUnbindDIO.builder().bindId(xnoBindId3).build();
                axbUnbindDIOList.add(axbUnbindDIO);
            }
        }, executor);

        CompletableFuture.allOf(queryXnoBindFuture,queryXnoBindFuture2,queryXnoBindFuture3).join();

        if(CollectionUtil.isNullOrEmpty(axbUnbindDIOList)){
            return Boolean.FALSE;
        }

        ResponseDTO<Collection<AxbUnbindDRO>> unbindResponse = axbBindRemoteService.unbind(axbUnbindDIOList);
        if (!unbindResponse.isSuccess() || CollectionUtil.isNullOrEmpty(unbindResponse.getData())) {
            log.error("[{}],解绑请求失败，失败原因：{}", TAG, unbindResponse.getMessage());

            return Boolean.FALSE;
        }

        Collection<AxbUnbindDRO> data = unbindResponse.getData();
        log.info("[{}],解绑返回response：{}", TAG, JSON.toJSONString(data));

        return Boolean.TRUE;
    }


    /**
     * 修改orderDetail
     * @param detailId
     * @param bindMobile
     * @param bindMobile2
     * @param bindMobile3
     */
    private Boolean updateOrderDetail(Long detailId, String bindMobile, String bindMobile2, String bindMobile3){
        OrderDetail updateOrderDetail = new OrderDetail();
        updateOrderDetail.setDetailId(detailId);

        if(StringUtil.isNotBlank(bindMobile)){
            updateOrderDetail.setBindTelephone(bindMobile);
        }
        if(StringUtil.isNotBlank(bindMobile2)){
            updateOrderDetail.setBindTelephone2(bindMobile2);
        }
        if(StringUtil.isNotBlank(bindMobile3)){
            updateOrderDetail.setBindTelephone3(bindMobile3);
        }
        updateOrderDetail.setBindStatus(OrderConsts.BIND_STATUS_BIND);
        updateOrderDetail.setUpdateTime(DateUtil.getNow());

        Integer integer = orderDetailService.updateOrderDetailByKey(updateOrderDetail);

        if(integer >0 ){
            // 删除绑定小号的缓存
            mapRedisTemplate.unlink(String.format(RedisKeyConsts.XNO_BIND_ORDER_TELEPHONE, detailId));

            return Boolean.TRUE;
        }

        return Boolean.FALSE;
    }


    /**
     * 获取orderDetail
     * @param orderId
     * @return
     */
    private OrderDetail getOrderDetail(Long orderId){
        OrderDetail cacheOrderDetail = orderDetailRedisTemplate.opsForValue().get(String.format(RedisKeyConsts.XNO_BIND_ORDER_DETAIL, orderId));
        if(Objects.isNull(cacheOrderDetail)){
            OrderDetail orderDetail = orderDetailService.findOrderDetailByKey(orderId);
            orderDetailRedisTemplate.opsForValue().set(String.format(RedisKeyConsts.XNO_BIND_ORDER_DETAIL, orderId),orderDetail,10, TimeUnit.MINUTES);

            return orderDetail;
        }

        return cacheOrderDetail;
    }

    /**
     * 获取orderWork
     * @param orderId
     * @param workId
     * @return
     */
    private OrderWork getOrderWork(Long orderId,Long workId){
        OrderWork cacheOrderWork = orderWorkRedisTemplate.opsForValue().get(String.format(RedisKeyConsts.XNO_BIND_ORDER_WORK, orderId, workId));
        if(Objects.isNull(cacheOrderWork)){
            OrderWork orderWork = orderWorkService.findOrderWorkByKey(orderId, workId);
            orderWorkRedisTemplate.opsForValue().set(String.format(String.format(RedisKeyConsts.XNO_BIND_ORDER_WORK, orderId, workId)),orderWork,10,TimeUnit.MINUTES);

            return orderWork;
        }

        return cacheOrderWork;
    }


    /**
     * 获取区号
     * @param cityId
     * @return
     */
    private String getAreaNum(Integer cityId) {
        String cacheAreaNum = redisTemplate.opsForValue().get(String.format(RedisKeyConsts.XNO_BIND_ORDER_AREA_NUM, cityId));
        if(StringUtil.isBlank(cacheAreaNum)){
            ResponseDTO<AreaDRO> areaDROResponse = areaListRemoteService.getById(cityId);
            if(!areaDROResponse.isSuccess()){
                log.error("[{}]，获取区号请求失败，失败原因：{}",TAG,areaDROResponse.getMessage());

                return GlobalConsts.SPLITER_EMPTY;
            }

            AreaDRO area = areaDROResponse.getData();
            log.info("[{}]，获取区号入参：{}，返回response：{}",TAG,cityId,JSON.toJSONString(area));

            if(Objects.nonNull(area)){
                redisTemplate.opsForValue().set(String.format(RedisKeyConsts.XNO_BIND_ORDER_AREA_NUM,cityId),area.getAreaNum(),10,TimeUnit.MINUTES);

                return area.getAreaNum();
            }

            return GlobalConsts.SPLITER_EMPTY;
        }

        return cacheAreaNum;
    }


    /**
     * 更新OrderDetail
     * @param orderDetail
     * @return
     */
    private Boolean updateOrderDetail(OrderDetail orderDetail){
        Map<String, String> telephoneMap = mapRedisTemplate.opsForValue().get(String.format(RedisKeyConsts.XNO_BIND_ORDER_TELEPHONE, orderDetail.getDetailId()));
        log.info("[{}]，telephoneMap：{}", TAG, JSON.toJSONString(telephoneMap));
        if (CollectionUtil.isNullOrEmpty(telephoneMap)) {
            return Boolean.FALSE;
        }

        Boolean updateStatus = this.updateOrderDetail(orderDetail.getDetailId(),
                StringUtil.isNotBlank(telephoneMap.get("bindMobile")) ? telephoneMap.get("bindMobile") : GlobalConsts.SPLITER_EMPTY,
                StringUtil.isNotBlank(telephoneMap.get("bindMobile2")) ? telephoneMap.get("bindMobile2") : GlobalConsts.SPLITER_EMPTY,
                StringUtil.isNotBlank(telephoneMap.get("bindMobile3")) ? telephoneMap.get("bindMobile3") : GlobalConsts.SPLITER_EMPTY
        );
        if (updateStatus) {
            return Boolean.TRUE;
        }

        return Boolean.FALSE;
    }
}
